
#include "PhongMarbleMaterial.h"
#include "HitRecord.h"
#include "PerlinNoise.h"
#include "Light.h"
#include "Math.h"
#include "PerlinNoise.h"
#include "Point.h"
#include "Primitive.h"
#include "Ray.h"
#include "RenderContext.h"
#include "Scene.h"
#include "Vector.h"
#include <iostream>
using namespace std;

PhongMarbleMaterial::PhongMarbleMaterial(const Color& c1, const Color& c2,
                                         double scale, int octaves, double tscale,
                                         double fscale, double lacunarity, double gain,
                                         float Kd, float Ka, const Color& phong_color,
                                         int phong_exponent)
  : c1(c1), c2(c2), scale(scale), octaves(octaves), tscale(tscale), fscale(fscale),
    lacunarity(lacunarity), gain(gain), Kd(Kd), Ka(Ka),
    phong_color(phong_color), phong_exponent(phong_exponent)
{
}

PhongMarbleMaterial::~PhongMarbleMaterial()
{
}

void PhongMarbleMaterial::shade(Color& result, const RenderContext& context, const Ray& ray,
                          const HitRecord& hit, const Color&, int) const
{
  const Scene* scene = context.getScene();
  const vector<Light*>& lights = scene->getLights();
  Point hitpos = ray.origin()+ray.direction()*hit.minT();
  Vector normal;
  hit.getPrimitive()->normal(normal, context, hitpos, ray, hit);
  double costheta = Dot(normal, ray.direction());
  if(costheta > 0){
    normal = -normal;
  } else {
    costheta = -costheta;
  }

  const Object* world = scene->getObject();
  Light*const* begin = &lights[0];
  Light*const* end = &lights[lights.size()];
  Color difflight = scene->getAmbient()*Ka;
  Color speclight(0, 0, 0);
  while(begin != end){
    Color light_color;
    Vector light_direction;
    double dist = (*begin++)->getLight(light_color, light_direction, context, hitpos);
    double cosphi = Dot(normal, light_direction);
    if(cosphi > 0){
      // Cast shadow rays...
      HitRecord shadowhit(dist);
      Ray shadowray(hitpos, light_direction);
      world->intersect(shadowhit, context, shadowray);
      if(!shadowhit.getPrimitive()){
        // No shadows...
        difflight += light_color*(Kd*cosphi);
        Vector H = light_direction - ray.direction();
        H.normalize();
        double cos_alpha = Dot(H, normal);
        if(cos_alpha > 0){
          double phong_term = ipow(cos_alpha, phong_exponent);
          speclight += light_color*phong_term;
        }
      }
    }
  }

  Vector uvw;
  hit.getPrimitive()->computeUVW(uvw, context, ray, hit);
  Point texturep(uvw*scale*fscale);
  double value = cos(uvw.x()*scale+tscale*turbulenceAbs(octaves, texturep, lacunarity, gain));
  value = value*0.5+0.5;
  Color color = c1*(1-value)+c2*value;
  result = difflight * color + speclight * phong_color;
}